﻿// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Validation.Properties;
using System.Reflection;
using System.ComponentModel;
using System.Globalization;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Integration
{
    /// <summary>
    /// Encapsulates the logic used to link validation for integration scenarios.
    /// </summary>
    public class ValidationIntegrationHelper
    {
        private IValidationIntegrationProxy integrationProxy;
        private Type validatedType;
        private PropertyInfo validatedProperty;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationIntegrationHelper"/> for an <see cref="IValidationIntegrationProxy"/>.
        /// </summary>
        /// <exception cref="ArgumentNullException">when <paramref name="integrationProxy"/> is <see langword="null"/>.</exception>
        /// <exception cref="InvalidOperationException">when configuration is not complete.</exception>
        public ValidationIntegrationHelper(IValidationIntegrationProxy integrationProxy)
        {
            if (integrationProxy == null)
            {
                throw new ArgumentNullException("integrationProxy");
            }

            validatedType = integrationProxy.ValidatedType;
            if (validatedType == null)
            {
                throw new InvalidOperationException(Resources.ExceptionIntegrationValidatedTypeNotAvailable);
            }

            string validatedPropertyName = integrationProxy.ValidatedPropertyName;
            if (string.IsNullOrEmpty(validatedPropertyName))
            {
                throw new InvalidOperationException(Resources.ExceptionIntegrationValidatedPropertyNameNotAvailable);
            }

            this.validatedProperty = validatedType.GetProperty(validatedPropertyName, BindingFlags.Public | BindingFlags.Instance);
            if (this.validatedProperty == null)
            {
                throw new InvalidOperationException(Resources.ExceptionIntegrationValidatedPropertyNotExists);
            }
            if (!this.validatedProperty.CanRead)
            {
                throw new InvalidOperationException(Resources.ExceptionIntegrationValidatedPropertyNotReadable);
            }

            this.integrationProxy = integrationProxy;
        }

        /// <summary>
        /// Returns the <see cref="Validator"/> represented by the configuration in the <see cref="IValidationIntegrationProxy"/>, linked
        /// with the integration scenario as necessary.
        /// </summary>
        public Validator GetValidator()
        {
            return PropertyValidationFactory.GetPropertyValidator(validatedType,
                validatedProperty,
                integrationProxy.Ruleset,
                integrationProxy.SpecificationSource,
                integrationProxy.GetMemberValueAccessBuilder());
        }

        /// <summary>
        /// Returns the value to validate.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007",
            Justification = "Generics are not appropriate here")]
        public bool GetValue(out object value, out string valueRetrievalFailureMessage)
        {
            value = null;

            value = this.integrationProxy.GetRawValue();
            PerformValueConversion(ref value, out valueRetrievalFailureMessage);

            return valueRetrievalFailureMessage == null;
        }

        private void PerformValueConversion(ref object value, out string valueRetrievalFailureMessage)
        {
            valueRetrievalFailureMessage = null;

            if (this.integrationProxy.ProvidesCustomValueConversion)
            {
                ValueConvertEventArgs eventArgs = new ValueConvertEventArgs(value,
                    this.validatedProperty.PropertyType,
                    this.integrationProxy,
                    this.validatedProperty.Name);
                this.integrationProxy.PerformCustomValueConversion(eventArgs);

                if (eventArgs.ConversionErrorMessage == null)
                {
                    value = eventArgs.ConvertedValue;
                }
                else
                {
                    value = null;
                    valueRetrievalFailureMessage = eventArgs.ConversionErrorMessage;
                }
            }
            else
            {
                if (value != null)
                {
                    if (value.GetType() == this.validatedProperty.PropertyType)
                    {
                        return;
                    }

                    try
                    {
                        TypeConverter converter = TypeDescriptor.GetConverter(this.validatedProperty.PropertyType);
                        value = converter.ConvertFrom(null, CultureInfo.CurrentCulture, value);
                    }
                    catch (Exception e)
                    {
                        if (e.InnerException is FormatException)
                        {
                            value = null;
                            valueRetrievalFailureMessage = string.Format(
                                CultureInfo.CurrentCulture,
                                Resources.ErrorCannotPerfomDefaultConversion,
                                value,
                                this.validatedProperty.PropertyType.FullName);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

        }
    }
}
